package govenor.overwatch;

import govenor.status.FileStoreStatus;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.hp.hpl.jena.rdf.model.Model;

import networkdescriptor.NetworkDescriptor;

import tcp.Watcher;

public class OverWatcher implements Watcher{
	private Map<String, FileStoreNode> registeredFileStoreNodes ;
	private Map<String, ClientDescriptor> registeredClients ;
	public static final String TYPE_HEARTBEAT_ND = "HB_NETWORK_DESCRIPTOR" ;
	public static final String TYPE_FILEUPLOAD_ND = "FU_NETWORK_DESCRIPTOR" ;
	public static final String TYPE_FILEDOWNLOAD_ND = "FD_NETWORK_DESCRIPTOR" ;
	public static final String TYPE_COMMAND_ND = "CM_NETWORK_DESCRIPTOR" ;
	
	public OverWatcher(){
		registeredFileStoreNodes = new LinkedHashMap<String, FileStoreNode>();
		registeredClients = new HashMap<String, ClientDescriptor>();
	}
	public void registerClient(String hostname, int commPort){
		if(!registeredClients.containsKey(hostname+":"+commPort)){
			registeredClients.put(hostname+":"+commPort, new ClientDescriptor(hostname, commPort));
		}
	}
	public void removeClient(String serverId){
		if(registeredClients.containsKey(serverId)){
			registeredClients.remove(serverId);
		}
	}
	private boolean removeRegisteredFileStoreNode(String id){
		if(registeredFileStoreNodes.containsKey(id)){
			synchronized (registeredFileStoreNodes) {
				registeredFileStoreNodes.remove(id);
				return true;
			}
		}else{
			return false;
		}
		
		
	}
	public Map<String, ClientDescriptor> getClientMap(){
		return this.registeredClients;
	}
	private boolean addRegisteredFileStoreNode(FileStoreNode fileStoreNode){
		String tempId = fileStoreNode.getHeartBeatNetworkDescription().getHostname()+":"+fileStoreNode.getHeartBeatNetworkDescription().getPortNumber();
		if(!registeredFileStoreNodes.containsKey(tempId)){
			registeredFileStoreNodes.put(tempId,fileStoreNode);
			return true;
		}else{
			return false;
		}
		
	}
	public List getListOfWatchedElements() {
		Set<String> keySet = registeredFileStoreNodes.keySet();
		Iterator<String> i = keySet.iterator();
		ArrayList<FileStoreNode> listOfNodes = new ArrayList<FileStoreNode>(registeredFileStoreNodes.size());
		while(i.hasNext()){
			listOfNodes.add(registeredFileStoreNodes.get(i.next()));
		}
		return listOfNodes;
	}
	public List getListOfWatchedElements(String type) {
		Set<String> keySet = registeredFileStoreNodes.keySet();
		Iterator<String> i = keySet.iterator();
		ArrayList listOfNodes = null;
		if(type != null && (type.equalsIgnoreCase(TYPE_HEARTBEAT_ND) || type.equalsIgnoreCase(TYPE_FILEUPLOAD_ND) || type.equalsIgnoreCase(TYPE_COMMAND_ND)|| type.equalsIgnoreCase(TYPE_FILEDOWNLOAD_ND))){
			listOfNodes = new ArrayList<NetworkDescriptor>(registeredFileStoreNodes.size()) ;
		}else{
			listOfNodes = new ArrayList<FileStoreNode>(registeredFileStoreNodes.size());
		}
		while(i.hasNext()){
			String keyValue = i.next();
			if(type != null && type.equalsIgnoreCase(TYPE_HEARTBEAT_ND)){
				listOfNodes.add(registeredFileStoreNodes.get(keyValue).getHeartBeatNetworkDescription());
			}else if(type != null && type.equalsIgnoreCase(TYPE_FILEUPLOAD_ND)){
				listOfNodes.add(registeredFileStoreNodes.get(keyValue).getFileUploadNetworkDescription());
			}else if(type != null && type.equalsIgnoreCase(TYPE_FILEDOWNLOAD_ND)){
				listOfNodes.add(registeredFileStoreNodes.get(keyValue).getFileDownloadNetworkDescription());
			}else if(type != null && type.equalsIgnoreCase(TYPE_COMMAND_ND)){
				listOfNodes.add(registeredFileStoreNodes.get(keyValue).getCommandNetworkDescription());
			}else{
				listOfNodes.add(registeredFileStoreNodes.get(keyValue));
			}
		}
		return listOfNodes;
	}
	public void addWatchedElement(FileStoreNode fileStoreNode){
		this.addRegisteredFileStoreNode(fileStoreNode);
	}
	public FileStoreNode getWatchedElement(String id){
		return this.registeredFileStoreNodes.get(id) ;
	}
	public void removeWatchedElement(String id) {
		boolean completed = this.removeRegisteredFileStoreNode(id);
		if(completed){
			System.out.println("File store node: "+id+" removed");
		}else{
			System.out.println("File store node: "+id+" could not be removed");
		}
		
	}
	public synchronized void updateStatusFromModel(String serverId,Model modelOfFiles){
		this.registeredFileStoreNodes.get(serverId).updateStatsForFilesInStore(modelOfFiles);
	}
	public Map<String,String> getAllFilesStored(){
		Set<String> setOfHostnames = this.registeredFileStoreNodes.keySet();
		Iterator<String> iter = setOfHostnames.iterator();
		HashMap<String,String> tempMap = new HashMap<String,String>();
		while(iter.hasNext()){
			String hostname = iter.next();
			List<String> filenames = this.registeredFileStoreNodes.get(hostname).getListOfFilenames();
			for(int i = 0; i < filenames.size(); i++){
				if(!tempMap.containsKey(filenames.get(i))){
					tempMap.put(filenames.get(i), hostname);
				}else{
					String tempServerId = tempMap.get(filenames.get(i));
					StringBuffer sb = new StringBuffer();
					sb.append(tempServerId);
					sb.append(":"+hostname);
					tempMap.put(filenames.get(i), sb.toString());
				}
			}
		}
		
		return tempMap;
	}
	public FileStoreStatus getFileStatusFromFileStore(String storeId, String filename){
		FileStoreNode node = this.registeredFileStoreNodes.get(storeId);
		return node.getFileStatus(filename);
	}
	public FileStoreNode getFileStoreNodeFromFileStore(String storeId){
		return this.registeredFileStoreNodes.get(storeId);
	}
	public int getNumberOfFileStores(){
		return this.registeredFileStoreNodes.size();
	}
	public String findBestFileStore(){
		Set<String> setOfServerIds = this.registeredFileStoreNodes.keySet();
		Iterator<String> iter = setOfServerIds.iterator();
		String bestServerId = "";
		FileStoreNode bestFileStore = null;
		String serverId = null;
		int i = 0 ;
		while(iter.hasNext()){
			serverId = iter.next();
			if(i == 0){
				bestFileStore = this.registeredFileStoreNodes.get(serverId);
				bestServerId = serverId;
			}else{
				FileStoreNode temp = this.registeredFileStoreNodes.get(serverId);
				bestFileStore = this.compareFileStoreNodes(temp, bestFileStore);
			}
		}
		bestServerId = bestFileStore.getHeartBeatNetworkDescription().getHostname()+":"+bestFileStore.getHeartBeatNetworkDescription().getPortNumber();
		return bestServerId;
	}
	private FileStoreNode compareFileStoreNodes(FileStoreNode fs1, FileStoreNode fs2){
		int count = 0;
		FileStoreStatus statusFs1 = fs1.getOverallStatus();
		FileStoreStatus statusFs2 = fs2.getOverallStatus();
		if(statusFs1.getNumberOfDownloads() < statusFs2.getNumberOfDownloads() ){
			count--;
		}else{
			count++;
		}
		if(statusFs1.getAverageServiceTime() < statusFs2.getAverageServiceTime() ){
			count--;
		}else{
			count++;
		}
		if(statusFs1.getQuickestServiceTime() < statusFs2.getQuickestServiceTime() ){
			count--;
		}else{
			count++;
		}
		if(statusFs1.getSlowestServiceTime() < statusFs2.getSlowestServiceTime() ){
			count--;
		}else{
			count++;
		}
		return (count < 0)?fs1:fs2;
	}
}
